package com.cee.admin.config.shiro.jwt;

import com.cee.admin.common.GlobalExceptionHandler;
import com.cee.admin.config.web.CorsProperties;
import com.cee.common.utils.SingleObjectUtils;
import com.cee.common.web.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.web.filter.authc.AuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;


@Slf4j
public class JwtFilter extends AuthenticationFilter {

    private TokenService tokenService;

    private GlobalExceptionHandler globalExceptionHandler;

    private CorsProperties corsProperties;

    public JwtFilter(TokenService tokenService, GlobalExceptionHandler globalExceptionHandler, CorsProperties corsProperties) {
        this.tokenService = tokenService;
        this.globalExceptionHandler = globalExceptionHandler;
        this.corsProperties = corsProperties;
    }

    public JwtFilter(TokenService tokenService, GlobalExceptionHandler globalExceptionHandler) {
        this.tokenService = tokenService;
        this.globalExceptionHandler = globalExceptionHandler;
    }

    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        // 跨域时会首先发送一个option请求，这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }

        return super.preHandle(request, response);
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpRequest = WebUtils.toHttp(request);
        String token = tokenService.getToken(httpRequest);
        JwtToken jwtToken = new JwtToken(token);
        // 提交给realm进行登入，如果错误他会抛出异常并被捕获
        try {
            getSubject(request, response).login(jwtToken);
        } catch (RefreshTokenException e) {
            //登录凭证准备过期
            log.error("token is ready to expire!", e);
            Result<?> result = globalExceptionHandler.handleRefreshTokenException(e);
            write(response, result);
            return false;
        } catch (ExpiredCredentialsException e) {
            //登录失效
            log.error("login verify is fail :", e);
            Result<?> result = globalExceptionHandler.handleExpiredCredentialsException(e);
            write(response, result);
            return false;
        } catch (DisabledAccountException e) {
            log.error("The disabled user tries to enter the system", e);
            tokenService.removeToken(token);
            Result<?> result = globalExceptionHandler.handleDisabledAccountException(e);
            write(response,result);
            return false;
        }
        return true;
    }

    private void write(ServletResponse response, Result<?> result) {
        try (PrintWriter writer = response.getWriter();) {
//            response.setContentType("application/json;charset=UTF-8");
//            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            writer.write(SingleObjectUtils.OM.writeValueAsString(result));
            writer.flush();
        } catch (Exception e) {
            log.error("shiro verification is fail and response is fail:", e);
            throw new RuntimeException(e);
        }
    }

}
